
// ToolsLib Project

/* ToolsLib library for RusRoute firewall and other projects of
 * Andrey A. Moiseenko / IE Moiseenko A.A. (Russia)
 * e-mail: support@maasoftware.ru, maa2002@mail.ru
 * web: http://maasoftware.ru, http://maasoftware.com, http://maasoft.ru, http://maasoft.org
 * Author's full name: Andrey Alekseevitch Moiseenko
 * (russian name: Моисеенко Андрей Алексеевич)
 */

// ToolsLib/mthrlib.cpp

/* Copyright (C) 2002-2024 Andrey A. Moiseenko (support@maasoftware.ru)
 * All rights reserved.
 *
 * This library contains cross-platform classes for working with
 * threads, mutexes and so on.
 * The library implementation written
 * by Andrey A. Moiseenko (support@maasoftware.ru).
 * This library and applications are
 * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
 * as long as the following conditions are aheared to.
 *
 * Copyright remains Andrey A. Moiseenko, and as such any Copyright notices in
 * the code are not to be removed.  If this code is used in a product,
 * Andrey A. Moiseenko should be given attribution as the author of the parts used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Andrey A. Moiseenko (support@maasoftware.ru)
 *
 * THIS SOFTWARE IS PROVIDED BY ANDREY A. MOISEENKO ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

//#include "stdafx.h"
#include "perm.h"
#ifdef _WIN32
#ifndef ___ToolsLibQT___
#include "ToolsLib\temp.h"
#else
#include "temp.h"
#endif
#else
#include "temp.h"
#endif

#ifdef TOOLSLIB_COLLECT_HASH_TABLES_ALLOC_SIZE
#ifdef TOOLSLIB_SINGLE_THREAD
_qword gHashTablesAllocSize = 0;
#else
std::atomic<_qword> gHashTablesAllocSize = 0;
#endif
#endif

#if 000
CMaa_gLock_lib_Mutex* __GLock__lib(bool bInit) noexcept
{
    static CMaa_gLock_lib_Mutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaa_gLock_lib_Mutex;
#ifndef TOOLSLIB_FAST_gLock_lib_Mutex
        gptr->AddRef();
#endif
    }
    return gptr;
}

CMaaMutex* __GLock__lib2(bool bInit) noexcept
{
    static CMaaMutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaMutex;
#ifndef TOOLSLIB_FAST_MUTEXES
        gptr->AddRef();
#endif
    }
    return gptr;
}

CMaa_gLock_usr_Mutex* __GLock__usr(bool bInit) noexcept
{
    static CMaa_gLock_usr_Mutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaa_gLock_usr_Mutex;
#ifndef TOOLSLIB_FAST_gLock_usr_Mutex
        gptr->AddRef();
#endif
    }
    return gptr;
}

CMaaMutex* __GLock__usr2(bool bInit) noexcept
{
    static CMaaMutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaMutex;
#ifndef TOOLSLIB_FAST_MUTEXES
        gptr->AddRef();
#endif
    }
    return gptr;
}

CMaa_gLock_usr3_Mutex* __GLock__usr3(bool bInit) noexcept
{
    static CMaa_gLock_usr3_Mutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaa_gLock_usr3_Mutex;
#ifndef TOOLSLIB_FAST_gLock_usr3_Mutex
        gptr->AddRef();
#endif
    }
    return gptr;
}

CMaaLiteMutex* __GLock__AllocatorBasicLock(bool bInit) noexcept
{
    static CMaaLiteMutex * gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaLiteMutex;
        gptr->AddRef();
    }
    return gptr;
}
#endif


#ifdef _WIN32
/*
CMaaLiteMutex::CMaaLiteMutex(BOOL fInitiallyLocked)
{
     m_hMutex = CreateMutex(nullptr, fInitiallyLocked, nullptr);
}
CMaaLiteMutex::~CMaaLiteMutex()
{
     CloseHandle(m_hMutex);
}
void CMaaLiteMutex::Lock()
{
     WaitForSingleObject(m_hMutex, INFINITE);
}
void CMaaLiteMutex::UnLock()
{
     ReleaseMutex(m_hMutex);
}
*/
#else

#endif

//===========================================================
#ifndef TOOLSLIB_SINGLE_THREAD
CMaaAtomicFastMutex::CMaaAtomicFastMutex(int Spins) noexcept
:   m_Lock(0),
    m_ThreadId(InvalidThrId),
    m_Spins(/*CMaaGetCpuCount() > 1 &&*/ Spins > 0 ? Spins : 1)
{
}
CMaaAtomicFastMutex::~CMaaAtomicFastMutex()
{
    if  (m_Lock.load() != 0)
    {
        // error: locked
    }
}
_dword CMaaAtomicFastMutex::Lock() noexcept
{
#ifdef _WIN32
    const DWORD ThreadId = GetCurrentThreadId();
    if  (m_ThreadId.load() == ThreadId)
#else
    const pthread_t ThreadId = pthread_self();
    const pthread_t t = m_ThreadId.load();
    if  (t != InvalidThrId && pthread_equal(t, ThreadId))
#endif
    {
        ++m_Lock;
    }
    else
    {
        int s = m_Spins;
        while(1)
        {
            int y = 0;
            if  ((--s ?
                m_Lock.compare_exchange_weak(y, 1) :
                m_Lock.compare_exchange_strong(y, 1)))
            {
                break;
            }
            if  (!s)
            {
                s = m_Spins > 1 ? DEFAULT_FAST_MUTEX_TRY_NEXT_SPINS : 1;
#ifdef _WIN32
                DWORD abThreadId = m_ThreadId.load();
                if  (abThreadId != InvalidThrId)
                {
                    const HANDLE h = OpenThread(SYNCHRONIZE, FALSE, abThreadId);
                    if  (!h)
                    {
                        if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
                        {
                            m_Lock.store(1);
                            return WAIT_ABANDONED;
                        }
                    }
                    else
                    {
                        CloseHandle(h);
                        if  (!SwitchToThread())
                        {
                            Sleep(1);
                        }
                    }
                }
#else
                pthread_t abThreadId = m_ThreadId.load();
                if  (abThreadId != InvalidThrId)
                {
                    if  (pthread_kill(abThreadId, 0) != 0)
                    {
                        if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
                        {
                            m_Lock.store(1);
                            return WAIT_ABANDONED;
                        }
                    }
                    else
                    {
                        if  (sched_yield())
                        {
                            usleep(1000);
                        }
                    }
                }
#endif
            }
        }
        m_ThreadId.store(ThreadId);
    }
    return WAIT_OBJECT_0;
}
_dword CMaaAtomicFastMutex::Lock(_dword ms) noexcept
{
    if  (ms == INFINITE)
    {
        return Lock();
    }
    if  (!ms)
    {
        return TryLock() ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
    }
    return Lock_us(1000 * (_uqword)ms);
}
_dword CMaaAtomicFastMutex::Lock_us(_qword us) noexcept
{
    if  (us == -1)
    {
        return Lock();
    }
    if  (!us)
    {
        return TryLock() ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
    }
#ifdef _WIN32
    const DWORD ThreadId = GetCurrentThreadId();
    const CMaaTime &tt = gHRTime;
    const _uqword nxt = tt.GetNextTime(us);
    if  (m_ThreadId.load() == ThreadId)
#else
    const pthread_t ThreadId = pthread_self();
    const CMaaTime & tt = gHRTime;
    const _uqword nxt = tt.GetNextTime(us);
    const pthread_t t = m_ThreadId.load();
    if  (t != InvalidThrId && pthread_equal(t, ThreadId))
#endif
    {
        ++m_Lock;
    }
    else
    {
        int s = m_Spins;
        while(1)
        {
            int y = 0;
            const bool b = (tt.GetCounter() >= nxt);
            if  ((--s && !b ?
                m_Lock.compare_exchange_weak(y, 1) :
                m_Lock.compare_exchange_strong(y, 1)))
            {
                break;
            }
            if  (b)
            {
                return WAIT_TIMEOUT;
            }
            if  (!s)
            {
                s = m_Spins > 1 ? DEFAULT_FAST_MUTEX_TRY_NEXT_SPINS : 1;
#ifdef _WIN32
                DWORD abThreadId = m_ThreadId.load();
                if  (abThreadId != InvalidThrId)
                {
                    const HANDLE h = OpenThread(SYNCHRONIZE, FALSE, abThreadId);
                    if  (!h)
                    {
                        if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
                        {
                            m_Lock.store(1);
                            return WAIT_ABANDONED;
                        }
                    }
                    else
                    {
                        CloseHandle(h);
                        if  (!SwitchToThread())
                        {
                            Sleep(1);
                        }
                    }
                }
#else
                pthread_t abThreadId = m_ThreadId.load();
                if  (abThreadId != InvalidThrId)
                {
                    if  (pthread_kill(abThreadId, 0) != 0)
                    {
                        if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
                        {
                            m_Lock.store(1);
                            return WAIT_ABANDONED;
                        }
                    }
                    else
                    {
                        if  (sched_yield())
                        {
                            usleep(1000);
                        }
                    }
                }
#endif
            }
        }
        m_ThreadId.store(ThreadId);
    }
    return WAIT_OBJECT_0;
}
bool CMaaAtomicFastMutex::TryLock() noexcept
{
#ifdef _WIN32
    const DWORD ThreadId = GetCurrentThreadId();
    DWORD abThreadId = m_ThreadId.load();
    if  (abThreadId == ThreadId)
#else
    const pthread_t ThreadId = pthread_self();
    pthread_t abThreadId = m_ThreadId.load();
    if  (abThreadId != InvalidThrId && pthread_equal(abThreadId, ThreadId))
#endif
    {
        ++m_Lock;
        return true;
    }
    int y = 0;
    if  (m_Lock.compare_exchange_strong(y, 1))
    {
        m_ThreadId.store(ThreadId);
        return true;
    }
#ifdef _WIN32
    if  (abThreadId != InvalidThrId)
    {
        const HANDLE h = OpenThread(SYNCHRONIZE, FALSE, abThreadId);
        if  (!h)
        {
            if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
            {
                m_Lock.store(1);
                return true; // WAIT_ABANDONED;
            }
        }
        else
        {
            CloseHandle(h);
        }
    }
#else
    if  (abThreadId != InvalidThrId)
    {
        if  (pthread_kill(abThreadId, 0) != 0)
        {
            if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
            {
                m_Lock.store(1);
                return true; // WAIT_ABANDONED;
            }
        }
    }
#endif
    return false;
}
int CMaaAtomicFastMutex::UnLock() noexcept
{
#ifdef _WIN32
    const DWORD ThreadId = GetCurrentThreadId();
    if  (m_ThreadId.load() == ThreadId)
#else
    const pthread_t ThreadId = pthread_self();
    const pthread_t t = m_ThreadId.load();
    if  (t != InvalidThrId && pthread_equal(t, ThreadId))
#endif
    {
        if  (m_Lock.load() == 1)
        {
            m_ThreadId.store(InvalidThrId);
        }
        return --m_Lock;
    }
    return -1;
}
void CMaaAtomicFastMutex::AddRef() const noexcept
{
    /*
    Lock();
    ++m_RefCount;
    UnLock();
    */
}
int CMaaAtomicFastMutex::UnRef() const noexcept
{
    /*
    Lock();
    --m_RefCount;
    return UnLock();
    */
    return 1;
}
bool CMaaAtomicFastMutex::GetLockHolder(int x, char* txt, int buffer_len) const noexcept
{
    return false;
}
_dword CMaaAtomicFastMutex::Lock(const char* txt) noexcept
{
    return Lock();
}
_dword CMaaAtomicFastMutex::LockF(const char* file, int line) noexcept
{
    return Lock();
}
int CMaaAtomicFastMutex::UnLockF(const char* file, int line) noexcept
{
    return UnLock();
}
void CMaaAtomicFastMutex::FlushLog(bool bForced) const noexcept
{
}
//===========================================================
CMaaAtomicFastMutex1::CMaaAtomicFastMutex1(/*int Spins = DEFAULT_FAST_MUTEX_SPINS*/) noexcept
:   m_Lock(0),
    m_ThreadId(InvalidThrId),
    m_Spins(/*CMaaGetCpuCount() > 1 ?*/ DEFAULT_FAST_MUTEX_SPINS /*: 1*/)
{
}
CMaaAtomicFastMutex1::~CMaaAtomicFastMutex1()
{
    if  (m_Lock.load() != 0)
    {
        // error: locked
    }
}
_dword CMaaAtomicFastMutex1::Lock() noexcept
{
#ifdef _WIN32
    const DWORD ThreadId = GetCurrentThreadId();
    if  (m_ThreadId.load() == ThreadId)
#else
    const pthread_t ThreadId = pthread_self();
    const pthread_t t = m_ThreadId.load();
    if  (t != InvalidThrId && pthread_equal(t, ThreadId))
#endif
    {
        ++m_Lock;
    }
    else
    {
        int s = m_Spins;
        while(1)
        {
            int y = 0;
            if  ((--s ?
                m_Lock.compare_exchange_weak(y, 1) :
                m_Lock.compare_exchange_strong(y, 1)))
            {
                break;
            }
            if  (!s)
            {
                s = m_Spins > 1 ? DEFAULT_FAST_MUTEX_TRY_NEXT_SPINS : 1;
#ifdef _WIN32
                DWORD abThreadId = m_ThreadId.load();
                if  (abThreadId != InvalidThrId)
                {
                    HANDLE h = OpenThread(SYNCHRONIZE, FALSE, abThreadId);
                    if  (!h)
                    {
                        if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
                        {
                            m_Lock.store(1);
                            return WAIT_ABANDONED;
                        }
                    }
                    else
                    {
                        CloseHandle(h);
                        if  (!SwitchToThread())
                        {
                            Sleep(1);
                        }
                    }
                }
#else
                pthread_t abThreadId = m_ThreadId.load();
                if  (abThreadId != InvalidThrId)
                {
                    if  (pthread_kill(abThreadId, 0) != 0)
                    {
                        if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
                        {
                            m_Lock.store(1);
                            return WAIT_ABANDONED;
                        }
                    }
                    else
                    {
                        if  (sched_yield())
                        {
                            usleep(1000);
                        }
                    }
                }
#endif
            }
        }
        m_ThreadId.store(ThreadId);
    }
    return WAIT_OBJECT_0;
}
bool CMaaAtomicFastMutex1::TryLock() noexcept
{
#ifdef _WIN32
    const DWORD ThreadId = GetCurrentThreadId();
    DWORD abThreadId = m_ThreadId.load();
    if  (abThreadId == ThreadId)
#else
    const pthread_t ThreadId = pthread_self();
    pthread_t abThreadId = m_ThreadId.load();
    if  (abThreadId != InvalidThrId && pthread_equal(abThreadId, ThreadId))
#endif
    {
        ++m_Lock;
        return true;
    }
    int y = 0;
    if  (m_Lock.compare_exchange_strong(y, 1))
    {
        m_ThreadId.store(ThreadId);
        return true;
    }
#ifdef _WIN32
    if  (abThreadId != InvalidThrId)
    {
        const HANDLE h = OpenThread(SYNCHRONIZE, FALSE, abThreadId);
        if  (!h)
        {
            if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
            {
                m_Lock.store(1);
                return true; // WAIT_ABANDONED;
            }
        }
        else
        {
            CloseHandle(h);
        }
    }
#else
    if  (abThreadId != InvalidThrId)
    {
        if  (pthread_kill(abThreadId, 0) != 0)
        {
            if  (m_ThreadId.compare_exchange_strong(abThreadId, ThreadId))
            {
                m_Lock.store(1);
                return true; // WAIT_ABANDONED;
            }
        }
    }
#endif
    return false;
}
int CMaaAtomicFastMutex1::UnLock() noexcept
{
#ifdef _WIN32
    const DWORD ThreadId = GetCurrentThreadId();
    if  (m_ThreadId.load() == ThreadId)
#else
    const pthread_t ThreadId = pthread_self();
    const pthread_t t = m_ThreadId.load();
    if  (t != InvalidThrId && pthread_equal(t, ThreadId))
#endif
    {
        if  (m_Lock.load() == 1)
        {
            m_ThreadId.store(InvalidThrId);
        }
        return --m_Lock;
    }
    return -1;
}
void CMaaAtomicFastMutex1::AddRef() const noexcept
{
}
int CMaaAtomicFastMutex1::UnRef() const noexcept
{
    return 1;
}
#ifdef _WIN32_000
DWORD Lock(DWORD to) noexcept
{
    if  (!to)
    {
        return TryLock() ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
    }
    return Lock();
}
#endif
bool CMaaAtomicFastMutex1::GetLockHolder(int x, char* txt, int buffer_len) const noexcept
{
    return false;
}
_dword CMaaAtomicFastMutex1::Lock(const char* txt) noexcept
{
    return Lock();
}
_dword CMaaAtomicFastMutex1::LockF(const char* file, int line) noexcept
{
    return Lock();
}
int CMaaAtomicFastMutex1::UnLockF(const char* file, int line) noexcept
{
    return UnLock();
}
void CMaaAtomicFastMutex1::FlushLog(bool bForced) const noexcept
{
}
#endif
//===========================================================

#ifdef __unix__

void SetThreadName(_dword /*ThreadId*/, const char * /*Name*/)
{
}

#ifndef TOOLSLIB_SINGLE_THREAD
CMaaSemaphore::CMaaSemaphore(int MaxCount, int InitialCount) noexcept // -1 is substituted by MaxCount
:   m_RefCount(0)
{
    sem_init(&m_hSemaphore, 0, InitialCount < 0 ? MaxCount : InitialCount);
}
CMaaSemaphore::~CMaaSemaphore() noexcept
{
    sem_destroy(&m_hSemaphore);
}
//DWORD Lock(const char * txt);
DWORD CMaaSemaphore::Lock(DWORD dwTimeToWait) noexcept
{
    sem_wait(&m_hSemaphore);
    ++m_RefCount;
    return 0;
}
//DWORD CMaaSemaphore::TryLock() noexcept;
void CMaaSemaphore::UnLock(int Count) noexcept
{
    for (int i = 0; i < Count; i++)
    {
        --m_RefCount;
        sem_post(&m_hSemaphore);
    }
}
#endif

CMaaMutex1::CMaaMutex1(bool bRecrsive) noexcept
{
    m_RefCount = 1;
    memset(&m, 0, sizeof(m));
    pthread_mutexattr_t a;
    pthread_mutexattr_init(&a);

    if  (bRecrsive)
    {
        //pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE_NP);
        pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
    }
    else
    {
        //pthread_mutexattr_settype(&a, PTHREAD_MUTEX_TIMED_NP); // NORMAL
        pthread_mutexattr_settype(&a, PTHREAD_MUTEX_ERRORCHECK); // NORMAL
    }
    pthread_mutex_init(&m, &a);

    pthread_mutexattr_destroy(&a);
}

CMaaMutex1::~CMaaMutex1() noexcept
{
    pthread_mutex_destroy(&m);
}

_dword CMaaMutex1::Lock() noexcept
{
    /*int n = */pthread_mutex_lock(&m);
    //printf("n = %d\n", n);
    ++m_RefCount;
    return 0;
}

int CMaaMutex1::UnLock() noexcept
{
    int x = --m_RefCount; // TODO: check result
    /*int n = */pthread_mutex_unlock(&m);
    //printf("n = %d\n", n);
    return x;
}

void CMaaMutex1::AddRef() noexcept
{
    Lock();
    ++m_RefCount;
    UnLock();
}
int CMaaMutex1::UnRef() noexcept
{
    Lock();
    --m_RefCount;
    return UnLock();
}

CMaaMutex2::CMaaMutex2() noexcept
:   CMaaMutex1(true)
{
    n = 0;
#ifdef _DEBUG_MTX
    for (int i = 0; i < 20; i++)
    {
        ff[i] = ff2[i] = "?";
        ll[i] = ll2[i] = -1;
    }
#endif
}

CMaaMutex2::~CMaaMutex2() noexcept
{
}

_dword CMaaMutex2::LockF(const char * f, int l) noexcept
{
    Lock();
    int x = n++;
#ifdef _DEBUG_MTX
    if  (x >= 0 && x < 20)
    {
        ff[x] = f;
        ll[x] = l;
        ff2[x] = ".";
        ll2[x] = -1;
    }
#endif
    return 0;
}

int * gpf = nullptr;

int CMaaMutex2::UnLockF(const char * f, int l) noexcept
{
    int x = --n;
#ifdef _DEBUG_MTX
    if  (x >= 0 && x < 20)
    {
        ff2[x] = f;
        ll2[x] = l;
        if  (this == &gLock)
        {
            //          printf("gLock.UnLock(%s, %d), previous lock was from %s, %d\n", ff2[x], ll2[x], ff[x], ll[x]); fflush(stdout);
        }
        else
        {
            //          printf("%p.UnLock(%s, %d), previous lock was from %s, %d\n", this, ff2[x], ll2[x], ff[x], ll[x]); fflush(stdout);
        }
    }
    if  (x < 0)
    {
        *gpf = 0;
    }
#endif
    CMaaMutex1::UnLock();
    return x;
    //printf("UnLockF(%s,%d), x = %d ......\n", f, l, x); fflush(stdout);
}

int CMaaMutex2::GetDepthEx() noexcept
{
    LockF(__FILE__, __LINE__);
    int n_ = n - 1;
    UnLock();
    return n_;
}

bool CMaaMutex2::GetLockHolder(int x, char * txt, int buffer_len) noexcept
{
    if  (x < 0 || x >= n || buffer_len < 100)
    {
        return false;
    }

#ifdef _DEBUG_MTX
    {
        sprintf(txt, "[%d]: ", x);
        int n0 = strlen(txt);
        const char * FileName = ff2[x];
        int LineNum = ll2[x];
        FileName = FileName ? FileName : "null";

        int n = (int)strlen(FileName);
        n = n0 + n < buffer_len - 30 ? n : buffer_len - 30 - n0;
        n = n >= 0 ? n : 0;
        memcpy(n0 + txt, FileName, n);
        sprintf(n0 + n + txt, "(%d)", LineNum);
    }
#else
    sprintf(txt, "[%d]: ???", x);
#endif
    return true;
}

int CMaaMutex2::GetRefCount() noexcept
{
    return m_RefCount;
}



#endif

/*
#ifdef _WIN32
Lock_Function(AtomicLock, CMaaMutex1, nullptr, nullptr)
#else
Lock_Function(AtomicLock, CMaaMutex1, nullptr, nullptr)
#endif
*/

/*
static CMaaMutex1* __GLock__Atomic() noexcept
{
    static CMaaMutex1* gptr = nullptr;
    if (!gptr)
    {
        gptr = TL_NEW_ CMaaMutex1;
        gptr->AddRef();
    }
    return gptr;
}
*/
#if 000
CMaaLiteMutex* __GLock__Atomic(bool bInit) noexcept
{
    static CMaaLiteMutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaLiteMutex;
        gptr->AddRef();
    }
    return gptr;
}

CMaaLiteMutex* __GLock__Atomic2(bool bInit) noexcept
{
    static CMaaLiteMutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaLiteMutex;
        gptr->AddRef();
    }
    return gptr;
}
#endif

#ifndef TOOLSLIB_SINGLE_THREAD
#ifdef _WIN32
unsigned __stdcall CMaaThread::RunThread(void * arg)
#else
// __unix__
void * CMaaThread::RunThread(void * arg)
#endif
{
    CMaaThread * p = (CMaaThread *)arg;

    unsigned r = (unsigned) -1;
    try
    {
        r = p->Run0();
    }
    catch(...)
    {
        if  (p->m_Point == (int)0xaa5555aa)
        {
        }
        else
        {
        }
    }
    //CProtocolListColor c(CProtocolListColor::eGreen);
    //printf("Exiting thread with id=%x, code=%d\n", GetCurrentThreadId(), r);

    //printf("CMaaThread::RunThread(): %p: returning %d\n", arg, r);

#ifdef _WIN32
    return r;
#else
    //return (void *)r;
    return reinterpret_cast<void *>(static_cast<long long>(r)); // !!WARNING!!
#endif
}

void CMaaThread::SetPriority(int /*pr*/, int sched_fifo)
{
#ifdef __unix__
    struct sched_param sp;
    //printf("sched_getscheduler(getpid()) returns %d\n", sched_getscheduler(getpid()));
    //fflush(stdout);
    int x = -1;
    if  (sched_fifo)
    {
        sp.sched_priority = 50; // 0..99
        x = sched_setscheduler(getpid(), SCHED_FIFO, &sp);
    }
    else
    {
        sp.sched_priority = 0;
        x = sched_setscheduler(getpid(), SCHED_OTHER, &sp);
    }
#endif
}

CMaaThread::CMaaThread() noexcept
{
#ifdef _WIN32
    m_hExit = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    m_ThreadId = CMaaInvalidThreadId();
    m_hThread = INVALID_HANDLE_VALUE;
#else
    // __unix__
    pthread_attr_init(&m_attr);
#ifndef ___ToolsLibQTAndroid___
    pthread_attr_setinheritsched(&m_attr, PTHREAD_EXPLICIT_SCHED);
#endif
    m_Handle = CMaaInvalidThreadId();
    m_bExit = false;

    /*
    int shed_policy;
    if  (!pthread_attr_getschedpolicy(&a, &shed_policy))
    {
        int prio_max = sched_get_priority_max(shed_policy);
        if  (prio_max != -1)
        {
            sched_param sp;
            sp.sched_priority = prio_max; // == 0 for SCHED_OTHER
            //
            printf("prio_max = %d\n", prio_max);
            pthread_attr_setinheritsched(&a, PTHREAD_EXPLICIT_SCHED);
            pthread_attr_setschedparam(&a, &sp);
        }
    }
    pthread_attr_setinheritsched(&a2, PTHREAD_EXPLICIT_SCHED);

    pthread_t t, t2;

    //int code = pthread_create(&t, &a, StartProc, nullptr);
    */

#endif
    SetPoint();
}

bool CMaaThread::Create() noexcept
{
#ifdef __unix__
    int x = pthread_create(&m_Handle, &m_attr, RunThread, this);

    //printf("pthread_create: m_Handle = %p\n", (void *)m_Handle); fflush(stdout);

    //fprintf(ff, "CMaaThread::Create() %p - %d(%s)\r\n", this, x, x == 0 ? "OK" : "ERROR"); fflush(ff);

    return x == 0;

    // m_bExit = false;
#else
    m_hThread = (HANDLE) _beginthreadex (nullptr, 0, &RunThread, this, 0, (UINT*) &m_ThreadId);
    //printf ("Created new thread with Id = %x\n", m_ThreadId);
    return m_hThread != INVALID_HANDLE_VALUE;
#endif
}

unsigned CMaaThread::Join() noexcept
{
    void * p = nullptr;
#ifdef __unix__
    m_bExit = true;
    pthread_t t{ CMaaInvalidThreadId() };
    if  (m_Handle == t)
    {
        return 0;
    }

    //printf("pthread_join m_Handle = %p\n", (void *)m_Handle); fflush(stdout);

    pthread_join(m_Handle, &p);
    m_Handle = t; // QT found bug of re-join

#else
    if  (m_hExit)
    {
        SetEvent(m_hExit);
    }
    if  (m_hThread && m_hThread != INVALID_HANDLE_VALUE)
    {
        WaitForSingleObject(m_hThread, INFINITE); // == WAIT_OBJECT_0;
        return 0;
    }
#endif
    //return (unsigned)(unsigned long)p;
    return static_cast<unsigned>(reinterpret_cast<long long>(p));
}

//int g_rc = -123;

bool CMaaThread::Wait(DWORD MaxWaitTime) noexcept
{
    void * p = nullptr;
#ifdef __unix__
    // For unix wait infinitly

    //m_bExit = true;
    pthread_t t { CMaaInvalidThreadId() };
    if  (m_Handle == t)
    {
        return true;
    }

#if 12345

    timespec tspc;
    //__utf8_printf("--- %d\n", clock_gettime(CLOCK_REALTIME, &tspc));
    //__utf8_printf("%d.%09d %d\n", tspc.tv_sec, tspc.tv_nsec, time(nullptr));
    //#define __use_rt
#ifdef __use_rt
#ifdef CLOCK_REALTIME_HR
    //__utf8_printf("%d %d\n", clock_gettime(CLOCK_REALTIME_HR, &tspc), clock_gettime(CLOCK_REALTIME, &tspc));
#else
    //__utf8_printf("--- %d\n", clock_gettime(CLOCK_REALTIME, &tspc));
#endif
#else
    timeval tv;
#endif

    if  (
#ifdef __use_rt
#ifdef CLOCK_REALTIME_HR
    clock_gettime(CLOCK_REALTIME_HR, &tspc) == -1 &&
#endif
    clock_gettime(CLOCK_REALTIME, &tspc) == -1
#else
    gettimeofday(&tv, nullptr) == -1
#endif
    )
    {
        // Handle error
        tspc.tv_sec = time(nullptr);
        tspc.tv_nsec = 0;
    }
#ifndef __use_rt
    else
    {
        tspc.tv_sec = tv.tv_sec;
        tspc.tv_nsec = tv.tv_usec * 1000;
    }
#endif
    //__utf8_printf("%d.%09d %d\n", tspc.tv_sec, tspc.tv_nsec, time(nullptr));
    tspc.tv_nsec += (MaxWaitTime % 1000) * 1000000;
    tspc.tv_sec += MaxWaitTime / 1000;
    if  (tspc.tv_nsec >= 1000000000)
    {
        tspc.tv_sec++;
        tspc.tv_nsec -= 1000000000;
    }
    //    clock_gettime(CLOCK_REALTIME, &tspc);

    int rc;
    //while(1)
    {
#ifndef ___ToolsLibQTAndroid___
        rc = MaxWaitTime == 0 ? 	pthread_tryjoin_np(m_Handle, &p) :
        MaxWaitTime != (DWORD)-1 ? pthread_timedjoin_np(m_Handle, &p, &tspc) :
        pthread_join(m_Handle, &p);
        if  (!rc)
        {
            m_Handle = t; // QT found bug of re-join
        }
#else
        rc = pthread_join(m_Handle, &p); // !!!!!!!!!!!!!!! Android !!!!!!!!!!!!
        m_Handle = t; // QT found bug of re-join
#endif
        //__utf8_printf("rc=%d, errno=%d\n", rc, errno);
        //	if (!(rc && errno == EINPROGRESS))
        {
            //	    break;
        }
    }
    //__utf8_printf("r\n---------------\nc=%d, errno=%d\n", rc, errno);
    //__utf8_printf_flush();

    //g_rc = rc;

    if  (MaxWaitTime != (DWORD)-1)
    {
        return rc == 0;
    }
    //    return rc == 0;
#else
    pthread_join(m_Handle, &p);
    m_Handle = t; // QT found bug of re-join
#endif

    return true;

#else
    /*
     if (m_hExit)
     {
          SetEvent(m_hExit);
     }
     */
    if  (m_hThread && m_hThread != INVALID_HANDLE_VALUE)
    {
        return WaitForSingleObject(m_hThread, MaxWaitTime) == WAIT_OBJECT_0;
    }
    return true;
#endif
}

CMaaThread::~CMaaThread()
{
#ifdef _WIN32
    Exit();
    CloseHandle(m_hExit);
    if  (m_hThread && m_hThread != INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hThread);
    }
#else
    Join();
#endif
}

unsigned CMaaThread::Run0()
{
    return Run();
}

unsigned CMaaThread::Run()
{
    return 0;
}

bool CMaaThread::Exit(DWORD MaxWaitTime) noexcept
{
#ifdef _WIN32
    if  (m_hExit)
    {
        SetEvent(m_hExit);
    }
    if  (m_hThread && m_hThread != INVALID_HANDLE_VALUE)
    {
        return WaitForSingleObject(m_hThread, MaxWaitTime) == WAIT_OBJECT_0;
    }
    return false;
#else
    pthread_t t{ CMaaInvalidThreadId() };
    if  (m_Handle == t)
    {
        return false;
    }
    m_bExit = true;
    return Wait(MaxWaitTime);
    /*
    if   (MaxWaitTime == 0)
    {
        m_bExit = true;
        return false;
    }
    */
    //Join();
    //return true;
#endif
}

void CMaaThread::CheckPoint(DWORD WaitTime)
{
#ifdef _WIN32
    DWORD RetEvent = WaitForSingleObject(m_hExit, WaitTime);
    if  (RetEvent == WAIT_OBJECT_0 || RetEvent == WAIT_FAILED)
    {
        throw eExitThread ();
    }
#else
    if  (m_bExit)
    {
        //printf("throw eExitThread();\n"); fflush(stdout);
        throw eExitThread ();
    }
    if  (!WaitTime)
    {
        return;
    }
    timeval tv;
    gettimeofday(&tv, nullptr);
    for (;;)
    {
        usleep(1000);
        if  (m_bExit)
        {
            throw eExitThread ();
        }
        timeval tv2;
        gettimeofday(&tv2, nullptr);
        int ms = (tv2.tv_sec - tv.tv_sec) * 1000 + (tv2.tv_usec - tv.tv_usec) / 1000;
        if  (ms >= (int)WaitTime)
        {
            return;
        }
    }
#endif
}
#endif

//---------------------------------------------------------------------------------------------
CMaaTimedMutex::CMaaTimedMutex() noexcept
{
#ifdef __unix__
    pthread_cond_init(&m_cond, nullptr);
    m_Status = 0;
    //m_curr = nullptr;
    const pthread_t InvalidThrId{ CMaaInvalidThreadId() };
    m_curr = InvalidThrId;
#endif
}
CMaaTimedMutex::~CMaaTimedMutex() noexcept
{
#ifdef __unix__
    pthread_cond_destroy(&m_cond);
#endif
}
bool CMaaTimedMutex::Lock(_dword ms) noexcept
{
#ifdef __unix__
    if  (ms == (_dword)-1)
    {
        return Lock((_qword)-1);
    }
    return Lock((_qword)ms * (_qword)1000UL);
#else
    return CMaaMutex2::Lock(ms) == WAIT_OBJECT_0;
#endif
}
bool CMaaTimedMutex::Lock(_qword us) noexcept
{
#ifdef __unix__

    CMaaWin32Locker_aLocker_gLock_Atomic;
    aLocker_Lock;
    pthread_t curr = m_curr;
    aLocker_UnLock;
    const pthread_t InvalidThrId{ CMaaInvalidThreadId() };
    if  (curr != InvalidThrId && pthread_equal(curr, pthread_self()))
    {
        m_Status++;
        return true;
    }

    CMaaMutex2::Lock();
    int code = 0;
    while(m_Status > 0)
    {
        if  (us != (_qword)-1)
        {
            struct timeval tv;
            gettimeofday(&tv, nullptr);

            timespec ti;
            ti.tv_nsec = (tv.tv_usec + us % 1000000UL) * 1000UL;
            ti.tv_sec = tv.tv_sec + (us / 1000000UL) + (ti.tv_nsec / 1000000000);
            ti.tv_nsec %= 1000000000;
            code = pthread_cond_timedwait(&m_cond, &m, &ti);
            //code = pthread_mutex_timedlock(&m, &ti);
        }
        else
        {
            code = pthread_cond_wait(&m_cond, &m);
            //code = pthread_mutex_lock(&m);
        }
        if  (code == 0 && m_Status > 0)
        {
            // many vendors warn of spurios wakeups from
            // pthread_cond_wait(), especially after signal delivery,
            // even though POSIX doesn't allow for it... sigh
            continue;
        }
        break;
    }

    if  (code == 0)
    {
        m_curr = pthread_self();
        m_Status++;
    }

    //if       (code != 0)
    {
        CMaaMutex2::UnLock();
    }

    //if (code && code != ETIMEDOUT) report_error(code, "QWaitCondition::wait()", "cv wait");

    //printf("code = %d\n", code);
    return (code == 0);
#else
    if  (us == (_qword)-1)
    {
        return Lock((_dword)-1);
    }
    return Lock((_dword)(us / 1000UL));
#endif
}

bool CMaaTimedMutex::UnLock() noexcept
{
#ifdef __unix__
    CMaaWin32Locker_aLocker_gLock_Atomic;
    aLocker_Lock;
    pthread_t curr = m_curr;
    aLocker_UnLock;
    if  (!pthread_equal(curr, pthread_self()))
    {
        return false;
    }
    aLocker_Lock;
    if  (--m_Status == 0)
    {
        //m_curr = nullptr;
        const pthread_t InvalidThrId{ CMaaInvalidThreadId() };
        m_curr = InvalidThrId;
        aLocker_UnLock;
        //CMaaMutex1::UnLock();
        pthread_cond_signal(&m_cond);
    }
    else
    {
        aLocker_UnLock;
    }
    return true;
#else
    return CMaaMutex2::UnLock() >= 0;
#endif
}

#ifdef _WIN32
CMaaEvent::CMaaEvent(HANDLE h, bool bManualReset, int DoNotCloseEventHandle)
/*
#ifdef __unix__
:    m_Mutex(false),
     m_bManualReset(bManualReset),
     m_bState(bManualReset ? bInitialState : false),
#else
*/
:   m_bManualReset(bManualReset),
    //#endif
    m_nWaiters(0),
    m_nWakeups(0),
    m_nPulseCount(0)
{
    /*
#ifdef __unix__
     pthread_cond_init(&m_cond, nullptr);
#else
*/
    m_hEvent = h;
    m_ExternalEvent = DoNotCloseEventHandle;
    //#endif
}
#endif

CMaaEvent::CMaaEvent(bool bManualReset, bool bInitialState /*for ManualReset event*/)
#ifdef __unix__
:   m_Mutex(false),
    m_bManualReset(bManualReset),
    m_bState(bManualReset ? bInitialState : false),
#else
    :   m_bManualReset(bManualReset),
#endif
    m_nWaiters(0),
    m_nWakeups(0),
    m_nPulseCount(0)
{
#ifdef __unix__
    pthread_cond_init(&m_cond, nullptr);
#else
    m_hEvent = CreateEvent(nullptr, TRUE, bManualReset && bInitialState ? TRUE : FALSE, nullptr);
#endif
}
CMaaEvent::~CMaaEvent()
{
#ifdef __unix__
    pthread_cond_destroy(&m_cond);
#else
    if  (m_ExternalEvent <= 0)
    {
        CloseHandle(m_hEvent);
    }
#endif
}
void CMaaEvent::WakeOne() noexcept
{
#ifdef __unix__
    m_Mutex.Lock();
    m_nWakeups = m_nWakeups + 1 <= m_nWaiters ? m_nWakeups + 1 : m_nWaiters;
    m_nPulseCount = 0;
    if  (m_bManualReset)
    {
        m_bState = true;
    }
    pthread_cond_signal(&m_cond);
    m_Mutex.UnLock();
#else
    m_Mutex.LockM();
    m_nWakeups = m_nWakeups + 1 <= m_nWaiters ? m_nWakeups + 1 : m_nWaiters;
    m_nPulseCount = 0;
    SetEvent(m_hEvent);
    m_Mutex.UnLock();
#endif
}
void CMaaEvent::WakeAll() noexcept
{
#ifdef __unix__
    m_Mutex.Lock();
    m_nWakeups = m_nWaiters;
    m_nPulseCount = 0;
    if  (m_bManualReset)
    {
        m_bState = true;
    }
    pthread_cond_broadcast(&m_cond);
    m_Mutex.UnLock();
#else
    m_Mutex.LockM();
    m_nWakeups = m_nWaiters;
    m_nPulseCount = 0;
    SetEvent(m_hEvent);
    m_Mutex.UnLock();
#endif
}
bool CMaaEvent::Wait(_dword TimeMs) noexcept
{
#ifdef __unix__
    if  (TimeMs == (_dword)-1)
    {
        return Wait((_qword)-1);
    }
    return Wait((_qword)TimeMs * (_qword)1000UL);
#else
    m_Mutex.LockM();
    if  (m_nWaiters++ == 0 && !m_bManualReset)
    {
        m_nWakeups = m_nPulseCount = 0;
        ResetEvent(m_hEvent);
    }
    m_Mutex.UnLock();
    while(1)
    {
        DWORD dw = WaitForSingleObject(m_hEvent, TimeMs);
        if  (dw != WAIT_OBJECT_0)
        {
            DWORD xx = GetLastError();
            m_Mutex.LockM();
            m_nWaiters--;
            m_Mutex.UnLock();
            return false;
        }
        m_Mutex.LockM();
        if  (m_nWakeups <= 0 && !m_bManualReset)
        {
            ResetEvent(m_hEvent);
            m_Mutex.UnLock();
        }
        else
        {
            m_nWakeups = m_nWakeups > 0 ? m_nWakeups - 1 : 0;
            m_nWaiters--;
            if  (m_nPulseCount > 0)
            {
                if  (--m_nPulseCount == 0)
                {
                    ResetEvent(m_hEvent);
                }
            }
            if  (m_nWaiters <= 0 && !m_bManualReset)
            {
                ResetEvent(m_hEvent);
            }
            m_Mutex.UnLock();
            break;
        }
    }
    return true;
#endif
}

bool CMaaEvent::Wait(_qword TimeUs) noexcept
{
#ifdef __unix__
    int code = 0;
    m_Mutex.Lock();
    //m_nWaiters++;
    if  (m_nWaiters++ == 0 && !m_bManualReset)
    {
        m_nWakeups = m_nPulseCount = 0;
        // /**/ m_bState = false;
    }
    while(1)
    {
        if  (m_bManualReset && m_bState)
        {
            break;
        }
        if  (TimeUs != (_qword)-1)
        {
            struct timeval tv;
            gettimeofday(&tv, nullptr);

            timespec ti;
            ti.tv_nsec = (tv.tv_usec + TimeUs % 1000000UL) * 1000UL;
            ti.tv_sec = tv.tv_sec + (TimeUs / 1000000UL) + (ti.tv_nsec / 1000000000);
            ti.tv_nsec %= 1000000000;
            code = pthread_cond_timedwait(&m_cond, &m_Mutex.m, &ti);
        }
        else
        {
            code = pthread_cond_wait(&m_cond, &m_Mutex.m);
        }
        /*
          if   (code == 0 && wakeups == 0)
          {
               // many vendors warn of spurios wakeups from
               // pthread_cond_wait(), especially after signal delivery,
               // even though POSIX doesn't allow for it... sigh
               continue;
          }
          */
        break;
    }
    if  (code == 0)
    {
        m_nWakeups = m_nWakeups > 0 ? m_nWakeups - 1 : 0;
    }
    m_nWaiters--;
    /*
     if   ((m_nWaiters == 0 || m_nWakeups == 0) && !m_bManualReset)
     {
          m_bState = false;
     }
     */
    if  (code == 0 && m_nPulseCount > 0)
    {
        if  (--m_nPulseCount == 0)
        {
            m_bState = false;
        }
    }
    m_Mutex.UnLock();

    //if (code && code != ETIMEDOUT) report_error(code, "QWaitCondition::wait()", "cv wait");

    return (code == 0);
#else
    if  (TimeUs == (_qword)-1)
    {
        return Wait((_dword)-1);
    }
    return Wait((_dword)(TimeUs / 1000UL));
#endif
}

bool CMaaEvent::Reset() noexcept
{
    // Reset manual reset mutex
    if  (!m_bManualReset)
    {
        return false;
    }
#ifdef __unix__
    m_Mutex.Lock();
    m_bState = false;
    m_Mutex.UnLock();
#else
    ResetEvent(m_hEvent);
#endif
    return true;
}

bool CMaaEvent::Pulse() noexcept
{
    if  (m_bManualReset)
    {
        // wake all and reset
#ifdef __unix__
        m_Mutex.Lock();
        m_nWakeups = m_nWaiters;
        m_nPulseCount = m_nWakeups;
        if  (m_bManualReset && m_nWakeups > 0)
        {
            m_bState = true;
        }
        else if (m_bManualReset) // && m_nWakeups == 0)
        {
            m_bState = false;
        }
        pthread_cond_broadcast(&m_cond);
        m_Mutex.UnLock();
#else
        m_Mutex.LockM();
        m_nWakeups = m_nWaiters;
        m_nPulseCount = m_nWakeups;
        if  (m_nWakeups > 0)
        {
            SetEvent(m_hEvent);
        }
        else
        {
            ResetEvent(m_hEvent);
        }
        m_Mutex.UnLock();
#endif
    }
    else
    {
        // wake one and reset
#ifdef __unix__
        m_Mutex.Lock();
        m_nWakeups = /*m_nWakeups +*/ 1 <= m_nWaiters ? /*m_nWakeups +*/ 1 : 0 /*m_nWaiters*/;
        if  (m_nWakeups > 0)
        {
            m_nPulseCount = 1;
        }
        if  (m_bManualReset && m_nWakeups > 0)
        {
            m_bState = true;
        }
        else if (m_bManualReset) // && m_nWakeups == 0)
        {
            m_bState = false;
        }
        pthread_cond_signal(&m_cond);
        m_Mutex.UnLock();
#else
        m_Mutex.LockM();
        m_nWakeups = /*m_nWakeups +*/ 1 <= m_nWaiters ? /*m_nWakeups +*/ 1 : 0 /*m_nWaiters*/;
        if  (m_nWakeups > 0)
        {
            m_nPulseCount = 1;
            SetEvent(m_hEvent);
        }
        else
        {
            m_nPulseCount = 0;
            ResetEvent(m_hEvent);
        }
        m_Mutex.UnLock();
#endif
    }
    return true;
}
//---------------------------------------------------------------------------------------------
CMaaRWLock::CMaaRWLock() noexcept
:   m_AccessType(0),
    m_n(0),
    m_h(true, true)
{
    //m_h = CreateEvent(nullptr, TRUE, TRUE, nullptr);
    //m_hState = true;
}

CMaaRWLock::~CMaaRWLock() noexcept
{
}

bool CMaaRWLock::RLock(_dword Time, int n) noexcept
{
    bool x = m_m1.Lock(Time);
    if  (!x) //x != WAIT_OBJECT_0 && x != WAIT_ABANDONED)
    {
        return false;
    }
    m_m2.Lock();
    m_n += n;
    if  (m_n > 0)
    {
        m_AccessType = 1;
    }
    m_m2.UnLock();
    m_m1.UnLock();
    return true;
}

bool CMaaRWLock::RUnLock(int n) noexcept
{
    m_m2.Lock();
    if  (m_n < n)
    {
        m_m2.UnLock();
        return false;
    }
    if  ((m_n -= n) <= 0)
    {
        if  (m_AccessType > 0)
        {
            m_AccessType = 0;
        }
        //m_hState = true;
        m_h.WakeAll();
    }
    m_m2.UnLock();
    return true;
}

bool CMaaRWLock::WLock(_dword Time) noexcept
{
    bool x = m_m1.Lock(Time);
    if  (!x)//x != WAIT_OBJECT_0 && x != WAIT_ABANDONED)
    {
        return false;
    }
    while(1)
    {
        bool x = m_h.Wait(Time);
        if  (!x)
        {
            // time out
            m_m1.UnLock();
            return false;
        }
        if  (x)// == WAIT_OBJECT_0)
        {
            m_m2.Lock();
            int nn = m_n;
            if  (nn > 0)
            {
                //m_hState = false;
                m_h.Reset();
            }
            m_m2.UnLock();
            if  (nn <= 0)
            {
                break;
            }
        }
    }
    m_AccessType = -1;
    return true;
}

void CMaaRWLock::WUnLock() noexcept
{
    if  (m_AccessType < 0)
    {
        m_AccessType = 0;
    }
    m_m1.UnLock();
}

void CMaaRWLock::UnLock(int n) noexcept
{
    if  (m_AccessType < 0)
    {
        WUnLock();
    }
    else if (m_AccessType > 0)
    {
        RUnLock(n);
    }
    else
    {
        // error
    }
}
//---------------------------------------------------------------------------------------------
#ifdef _WIN32

CMaaRWLock2::CMaaRWLock2() noexcept
{
}
CMaaRWLock2::~CMaaRWLock2() noexcept
{
}
void CMaaRWLock2::RLock(int n) noexcept
{
    m_Lock.RLock((_dword)-1, n);
}
void CMaaRWLock2::RUnLock(int n) noexcept
{
    m_Lock.RUnLock(n);
}
void CMaaRWLock2::WLock() noexcept
{
    m_Lock.WLock((_dword)-1);
}
void CMaaRWLock2::WUnLock() noexcept
{
    m_Lock.WUnLock();
}

#else

CMaaRWLock2::CMaaRWLock2() noexcept
{
    pthread_mutex_init(&m_Mutex, NULL);
    pthread_cond_init(&m_CondRead, NULL);
    pthread_cond_init(&m_CondWrite, NULL);

    m_Readers = m_Writers = m_Read_waiters = m_Write_waiters = 0;
}
CMaaRWLock2::~CMaaRWLock2() noexcept
{
    pthread_cond_destroy(&m_CondWrite);
    pthread_cond_destroy(&m_CondRead);
    pthread_mutex_destroy(&m_Mutex);
}
void CMaaRWLock2::RLock(int n) noexcept
{
    pthread_mutex_lock(&m_Mutex);
    if  (m_Writers || m_Write_waiters)
    {
        m_Read_waiters += n;
        do
        {
            pthread_cond_wait(&m_CondRead, &m_Mutex);

        } while (m_Writers || m_Write_waiters);

        m_Read_waiters -= n;
    }
    m_Readers += n;
    pthread_mutex_unlock(&m_Mutex);
}
void CMaaRWLock2::RUnLock(int n) noexcept
{
    pthread_mutex_lock(&m_Mutex);
    m_Readers -= n;
    if  (m_Write_waiters)
    {
        pthread_cond_signal(&m_CondWrite);
    }
    pthread_mutex_unlock(&m_Mutex);
}
void CMaaRWLock2::WLock() noexcept
{
    pthread_mutex_lock(&m_Mutex);
    if  (m_Readers || m_Writers)
    {
        m_Write_waiters++;
        do
        {
            pthread_cond_wait(&m_CondWrite, &m_Mutex);

        } while (m_Readers || m_Writers);

        m_Write_waiters--;
    }
    m_Writers = 1;
    pthread_mutex_unlock(&m_Mutex);
}
void CMaaRWLock2::WUnLock() noexcept
{
    pthread_mutex_lock(&m_Mutex);
    m_Writers = 0;
    if  (m_Write_waiters)
    {
        pthread_cond_signal(&m_CondWrite);
    }
    else if (m_Read_waiters)
    {
        pthread_cond_broadcast(&m_CondRead);
    }
    pthread_mutex_unlock(&m_Mutex);
}
//---------------------------------------------------------------------------------------------
// Since C++ 17 (VS2015):
/*
#include <shared_mutex>

typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock >  WriteLock;
typedef std::shared_lock< Lock >  ReadLock;

Lock myLock;

void ReadFunction()
{
     ReadLock r_lock(myLock);
     //Do reader stuff
}

void WriteFunction()
{
     WriteLock w_lock(myLock);
     //Do writer stuff
}
*/
//---------------------------------------------------------------------------------------------

#endif
//---------------------------------------------------------------------------------------------

#if 0
void AtomicLock() noexcept
{
    //AtomicLock(true);
    __GLock__Atomic()->Lock();
}

void AtomicUnLock() noexcept
{
    //AtomicLock(false);
    __GLock__Atomic()->UnLock();
}

void AtomicLock2() noexcept
{
    //AtomicLock(true);
    __GLock__Atomic2()->Lock();
}

void AtomicUnLock2() noexcept
{
    //AtomicLock(false);
    __GLock__Atomic2()->UnLock();
}
#endif

#ifdef __unix__
//---------------------------------------------------------------------------------------------
#if 0
CMaaWin32Locker::CMaaWin32Locker(CMaaMutex &Lock, CMaaString
#ifndef _DEBUG_MTX
         &
#endif
         Name) noexcept
:   m_Lock(Lock)
#ifdef _DEBUG_MTX
    , m_Name(Name)
#endif
{
    m_x = 0;
    m_bUnLockerMode = false;
}
CMaaWin32Locker::CMaaWin32Locker(CMaaMutex &Lock, const char * Name) noexcept
:   m_Lock(Lock)
    //, m_Name(Name)
{
    m_x = 0;
    m_bUnLockerMode = false;
}
CMaaWin32Locker::CMaaWin32Locker(CMaaMutex &Lock) noexcept
:   m_Lock(Lock)
{
    m_x = 0;
    m_bUnLockerMode = false;
}
CMaaWin32Locker::~CMaaWin32Locker() noexcept
{
    if  ((m_x > 0 && !m_bUnLockerMode) || m_x != 0)
    {
        while(m_x > 0)
        {
            m_Lock.UnLockM();
            --m_x;
        }
        while(m_x < 0)
        {
            m_Lock.LockM();
            ++m_x;
        }
    }
}
#ifndef DEBUG_MTX
DWORD CMaaWin32Locker::Lock() noexcept
{
    //DWORD x = m_Lock.Lock();
#ifdef DEBUG_MTX
    m_Lock.LockF("unknown", -2);
#else
    m_Lock.Lock();
#endif
    //if   (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
    {
        ++m_x;
    }
    //     return x;
    return 0;
}
#endif
DWORD CMaaWin32Locker::Lock(const char * /*txt*/) noexcept
{
    //     DWORD x = m_Lock.Lock(txt);
    //DWORD x = m_Lock.Lock();
#ifdef DEBUG_MTX
    m_Lock.LockF("unknown", -2);
#else
    m_Lock.Lock();
#endif
    //if   (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
    {
        ++m_x;
    }
    //     return x;
    return 0;
}
/*
DWORD CMaaWin32Locker::Lock(DWORD dwTimeToWait)
{
     DWORD x = m_Lock.Lock(dwTimeToWait);
     if   (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
     {
          ++m_x;
     }
     return x;
}
*/
DWORD CMaaWin32Locker::LockF(const char * file, int line) noexcept
{
    //     DWORD x = m_Lock.LockF(file, line);
    //     if   (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
    if  (m_Lock.LockF(file, line))
    {
        ++m_x;
    }
    //     return x;
    return 0;
}
int CMaaWin32Locker::UnLock() noexcept
{
#ifdef DEBUG_MTX
    return UnLockF("unknown", -2);
#else
    if  (m_x <= 0 && !m_bUnLockerMode)
    {
        return -1;
    }
    --m_x;
    return m_Lock.UnLock();
#endif
}
int CMaaWin32Locker::UnLockF(const char * file, int line) noexcept
{
    if  (m_x <= 0 && !m_bUnLockerMode)
    {
        return -1;
    }
    --m_x;
    return m_Lock.UnLockF(file, line);
}
void CMaaWin32Locker::SetUnLockerMode(bool bSet) noexcept
{
    m_bUnLockerMode = bSet;
}
#endif
//---------------------------------------------------------------------------------------------
#endif


//==============================================================

/*
LONG __cdecl InterlockedCompareExchange(
  _Inout_ LONG volatile *Destination,
  _In_    LONG          Exchange,
  _In_    LONG          Comparand
)
{
     // Lock
     LONG r = *Destination;
     if (r == Comparand)
     {
          *Destination = Exchange;
     }
     // UnLock
     return r;
}
*/

#ifdef _WIN32
bool fnMaaSafeMakeStatic(LONG volatile *pFlag) noexcept
{
    if  (*pFlag == 2)
    {
        return false;
    }
    LONG f;
    while((f = InterlockedCompareExchange(pFlag, 1, 0)) == 1)
    {
        Sleep(1);
    }
    if  (f == 0)
    {
        return true;
    }
    //if (*pFlag == 2)
    //{
    return false;
    //}
}
void fnMaaSafeStaticMade(LONG volatile *pFlag) noexcept
{
    InterlockedCompareExchange(pFlag, 2, 1);
}
#else
bool fnMaaSafeMakeStatic(int volatile *pFlag, CMaa_gLock_lib_Mutex &m) noexcept
{
    if  (*pFlag == 2)
    {
        return false;
    }
    m.LockM();
    if  (*pFlag == 0)
    {
        *pFlag = 1;
        return true;
    }
    // error
    m.UnLockM();
    return false;
}
void fnMaaSafeStaticMade(int volatile *pFlag, CMaa_gLock_lib_Mutex &m) noexcept
{
    if  (*pFlag == 1)
    {
        *pFlag = 2;
        m.UnLockM();
    }
}
#endif

#ifdef _WIN32
CMaaSafeMakeStatic::CMaaSafeMakeStatic(LONG volatile *pFlag) noexcept
#else
CMaaSafeMakeStatic::CMaaSafeMakeStatic(int volatile *pFlag) noexcept
#endif
{
    m_pFlag = pFlag;
    m_f = 0;
}
bool CMaaSafeMakeStatic::Make() noexcept
{
    if  (m_f == 0)
    {
#ifdef _WIN32
        bool f = fnMaaSafeMakeStatic(m_pFlag);
#else
        bool f = fnMaaSafeMakeStatic(m_pFlag, gLock);
#endif
        m_f = f ? 1 : 2;
        return f;
    }
    return m_f == 1;
}

bool CMaaSafeMakeStatic::ToInit_() noexcept
{
    return m_f == 1;
}

CMaaSafeMakeStatic::~CMaaSafeMakeStatic() noexcept
{
    Done();
}

bool CMaaSafeMakeStatic::Done() noexcept
{
    if  (m_f == 1)
    {
#ifdef _WIN32
        fnMaaSafeStaticMade(m_pFlag);
#else
        fnMaaSafeStaticMade(m_pFlag, gLock);
#endif
        m_f = 2;
        return true;
    }
    return false;
}

#ifndef _WIN32
//CMaaMutex CMaaSafeMakeStatic::s_Mutex;
#endif

#if 0
CMaaStaticStringsAllocator gStaticStringsAllocator;

CMaaStaticStringsAllocator::CMaaStaticStringsAllocator(int N)
:   m_n(0),
    m(N, 1),
    m_bDontDel(false)
{
}
CMaaString & CMaaStaticStringsAllocator::Alloc(const char * str)
{
    CMaaString * p = TL_NEW CMaaString(str);
    if  (p)
    {
        CMaaWin32Locker_aLocker_gLock_Atomic;
        aLocker_Lock;
        try
        {
            m[m_n] = p;
            m_n++;
        }
        catch(...)
        {
        }
        aLocker_UnLock;
    }
    return *p;
}
CMaaString & CMaaStaticStringsAllocator::Alloc(CMaaString str)
{
    CMaaString * p = TL_NEW CMaaString(str);
    if  (p)
    {
        CMaaWin32Locker_aLocker_gLock_Atomic;
        aLocker_Lock;
        try
        {
            m[m_n] = p;
            m_n++;
        }
        catch(...)
        {
        }
        aLocker_UnLock;
    }
    return *p;
}
CMaaStaticStringsAllocator::~CMaaStaticStringsAllocator()
{
    if  (!m_bDontDel)
    {
        for (int i = m_n; i--; )
        {
            //__utf8_printf("delete m[%d];\n", i);
            delete m[i];
        }
    }
}
void CMaaStaticStringsAllocator::SetDontDel(bool f)
{
    m_bDontDel = f;
}
#endif

#if 0
MAA_STATIC_FLAG Flag1;
MAA_STATIC_FLAG Flag2;
MAA_STATIC_FLAG Flag3;
static CMaaMutex * pMutex = nullptr;

class CMaaThreadTestStaticCreate : public CMaaThread
{
    HANDLE &m_h;
    int m_n;
public:
    CMaaThreadTestStaticCreate(HANDLE &h, int n)
    :   m_h(h),
        m_n(n)
    {
    }
    unsigned Run()
    {
        WaitForSingleObject(m_h, INFINITE);
        {
            CMaaSafeMakeStatic lc1(&Flag3);
            /*
               if (lc1.Make())
               {
                    __utf8_printf("1(%d)\n", m_n);
                    return 1;
               }
               else
               {
                    __utf8_printf("0(%d)\n", m_n);
               }
               */
            lc1.Make();
            static CMaaString &x = gStaticStringsAllocator.Alloc("aaaaaaaaa");
            static CMaaString  y = gStaticStringsAllocator.Alloc("bbbbbbbbb");
            lc1.Done();
            __utf8_printf(" %02d %S %p %p\n", m_n, &x, &x, (const char *)x);
            //CMaaString xx = *x;
            //__utf8_printf("%S\n", x);
            //__utf8_printf("%S\n", &xx);
        }
        return 0;
    }
};

int m99()
{
    if  (fnMaaSafeMakeStatic(&Flag1))
    {
        pMutex = TL_NEW CMaaMutex();
        fnMaaSafeStaticMade(&Flag1);
        return 1;
    }
    //pMutex->
    return 0;
}
int main()
{
    /*__utf8_printf("%d ", m99());
     __utf8_printf("%d ", m99());
     __utf8_printf("%d\n", m99());*/
    __utf8_printf("%d %d %d\n", m99(), m99(), m99());

    {
        CMaaSafeMakeStatic lc1(&Flag2);
        if  (lc1.Make())
        {
            __utf8_printf("1\n");
        }
        else
        {
            __utf8_printf("0\n");
        }
    }
    CMaaSafeMakeStatic lc2(&Flag2);
    if  (lc2.Make())
    {
        __utf8_printf("1\n");
    }
    else
    {
        __utf8_printf("0\n");
    }

    HANDLE h = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    const int N = 20;
    CMaaThreadTestStaticCreate * pt[N];
    int i;
    for (i = 0; i < N; i++)
    {
        pt[i] = TL_NEW CMaaThreadTestStaticCreate(h, i);
        pt[i]->Create();
    }
    Sleep(100);
    SetEvent(h);
    Sleep(100);
    for (i = 0; i < N; i++)
    {
        delete pt[i];
    }
    CloseHandle(h);
    return 0;
}
#endif

static bool gCMaaToolLib_crt_Initialized = false;
void gCMaaToolLib_crt_Initializer() noexcept
{
    if (!gCMaaToolLib_crt_Initialized)
    {
        GetHRTime(true);
        __GLock__Atomic(true);
        __GLock__Atomic2(true);
        __GLock__lib(true);
        __GLock__lib2(true);
        __GLock__usr(true);
        __GLock__usr2(true);
        __GLock__usr3(true);
        __GLock__AllocatorBasicLock(true);
        //__GLock__sfnInitLock(true);
        __GLock__SyncAssignLockUpperLock(true);
        CMaaString aaa = CMaaTLGlobalString(CMaaTLGlobalStrings::e_max, true);
        CMaaString bbb = CMaaTLFGlobalString(CMaaTLFGlobalStrings::e_max, true);
        CMaaGetCpuCount();

        gCMaaToolLib_crt_Initialized = true;
    }
}

struct CMaaToolLib_crt_Initializer
{
    CMaaToolLib_crt_Initializer()
    {
        gCMaaToolLib_crt_Initializer();
        /*
        {
            CMaaWin32Locker<CMaaLiteMutex> l_2(gLock_Atomic2);
            l_2.Lock();
            CMaaWin32Locker<CMaaLiteMutex> l_1(gLock_Atomic);
            l_1.Lock();
            //l_1.UnLock();
            //l_2.UnLock();
        }
        __GLock__lib();
        __GLock__lib2();
        __GLock__usr();
        __GLock__usr2();
        __GLock__usr3();
        __GLock__AllocatorBasicLock();
        */

        CMaaString::GetStaticErrorString(-1);

        CMaaString* p0 = TL_NEW CMaaString(CMaaStringEta);
        CMaaString bbb(CMaaString0);
        bbb = CMaaStringWC0;
        bbb = CMaaStringWChar0;
        bbb = CMaaStringWC32_0;

        CMaaString s1, s2;
        s1.Format( "%d", 1);
        s2.Format2("%d", "%1", 1);

        CMaaXmlDocument doc(CMaaStringDoc, CMaaStringDoc);
        CMaaXmlElement e = doc.DocumentElement(); // doc.CreateElement("a");
        //e.AddAttribute("a", CMaaStringZ);
        e.AddAttribute(CMaaString_a /*CMaaStrCh('a', true)*/, CMaaStringZ);
        //e.FindNodeWithAttrRO("b", "c", "d");

#ifndef TOOLSLIB_SINGLE_THREAD
        CMaaConnectionInTimeCharacteristics* p1 = TL_NEW CMaaConnectionInTimeCharacteristics(1);
#ifndef _WIN32
        //HANDLE h3 = CreateEvent(nullptr, false, false, nullptr);
        //CloseHandle(h3);
#endif
        delete p1;
#endif
        delete p0;
    }
};

static CMaaToolLib_crt_Initializer s_CMaaToolLib_crt_Initializer;
